package org.lanqiao.ssm.common.pay.wexin.util;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

import com.tencent.common.Configure;
import com.tencent.common.RandomStringGenerator;
import com.tencent.common.Signature;
import com.tencent.common.Util;
import com.tencent.protocol.pay_query_protocol.ScanPayQueryResData;
import com.tencent.protocol.refund_protocol.RefundReqData;
import com.tencent.protocol.refund_protocol.RefundResData;
import com.tencent.protocol.refund_query_protocol.RefundQueryReqData;
import com.tencent.protocol.refund_query_protocol.RefundQueryResData;
import com.tencent.service.RefundQueryService;
import com.tencent.service.RefundService;
import com.tencent.util.AppSignUtil;
import com.tencent.util.UnifiedOrderRequest;
import com.tencent.util.UnifiedOrderRespose;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;

public class WeiXinPayHelper {

	/**
	 * 
	 * @Title: createOrderInfo
	 * @Description: 生成签名订单 用来调用生成预付订单
	 * @param orderId
	 * @param body
	 * @param total_fee
	 * @return
	 */
	public static String createOrderInfo(String orderId, String body, String total_fee) {
		// 生成订单对象
		UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();
		unifiedOrderRequest.setAppid(Configure.getAppid());// 公众账号ID
		unifiedOrderRequest.setMch_id(Configure.getMchid());// 商户号
		unifiedOrderRequest.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));// 随机字符串
		unifiedOrderRequest.setBody(body);// 商品描述
		unifiedOrderRequest.setOut_trade_no(orderId);// 商户订单号
		unifiedOrderRequest
				.setTotal_fee(new BigDecimal(total_fee).multiply(new BigDecimal(100)).toBigInteger().toString()); // 金额需要扩大100倍:1代表支付时是0.01
		unifiedOrderRequest.setSpbill_create_ip(Configure.getIP());// 终端IP
		unifiedOrderRequest.setNotify_url(Configure.getWEIXIN_NOTIFY_URL());// 通知地址
		unifiedOrderRequest.setTrade_type(Configure.TRADE_TYPE);// JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
		try {
			unifiedOrderRequest.setSign(Signature.getSign(unifiedOrderRequest));
			System.out.println(unifiedOrderRequest);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} // 签名<span
			// style="color:#ff0000;"><strong>说明5(见文末，签名方法一并给出)</strong></span>
			// 将订单对象转为xml格式
		XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_"))); // <span
																							// style="color:#ff0000;"><strong>说明3(见文末)</strong></span>
		xStream.alias("xml", UnifiedOrderRequest.class);// 根元素名需要是xml

		return xStream.toXML(unifiedOrderRequest);
	}

	/**
	 * 生成订单
	 * 
	 * @param orderId
	 * @return
	 */
	public static String createQueryInfo(String orderId) {
		// 生成订单对象
		UnifiedOrderRequest unifiedOrderRequest = new UnifiedOrderRequest();
		unifiedOrderRequest.setAppid(Configure.getAppid());// 公众账号ID
		unifiedOrderRequest.setMch_id(Configure.getMchid());// 商户号
		unifiedOrderRequest.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));// 随机字符串
																							// <span
																							// style="color:#ff0000;"><strong>说明2(见文末)</strong></span>
		// unifiedOrderRequest.setNonce_str("5K8264IUJICH16QQQOKISI8ZNM48U8V3");//随机字符串
		// <span style="color:#ff0000;"><strong>说明2(见文末)</strong></span>
		unifiedOrderRequest.setOut_trade_no(orderId);// 商户订单号
		try {
			unifiedOrderRequest.setSign(Signature.getSign(unifiedOrderRequest));
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} // 签名<span
			// style="color:#ff0000;"><strong>说明5(见文末，签名方法一并给出)</strong></span>
			// 将订单对象转为xml格式
		XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_"))); // <span
																							// style="color:#ff0000;"><strong>说明3(见文末)</strong></span>
		xStream.alias("xml", ScanPayQueryResData.class);// 根元素名需要是xml
		return xStream.toXML(unifiedOrderRequest);
	}

	/**
	 * 调统一下单API
	 * 
	 * @param orderInfo
	 * @return
	 */
	public static UnifiedOrderRespose makeUnifiedOrder(String orderInfo) {
		try {
			HttpURLConnection conn = (HttpURLConnection) new URL(Configure.UNIFIED_ORDER_API).openConnection();
			// 加入数据
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);

			BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
			buffOutStr.write(orderInfo.getBytes());
			buffOutStr.flush();
			buffOutStr.close();

			// 获取输入流
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

			String line = null;
			StringBuffer sb = new StringBuffer();
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}

			XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));// 说明3(见文末)
			// 将请求返回的内容通过xStream转换为UnifiedOrderRespose对象
			xStream.alias("xml", UnifiedOrderRespose.class);

			UnifiedOrderRespose unifiedOrderRespose = (UnifiedOrderRespose) xStream.fromXML(sb.toString());

			// 根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url
			// <span style="color:#ff0000;"><strong>说明4(见文末)</strong></span>
			return unifiedOrderRespose;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 微信查询订单
	 * 
	 * @param orderInfo
	 * @return
	 */
	public static ScanPayQueryResData WeiXinOrderQuery(String orderInfo) {
		try {
			HttpURLConnection conn = (HttpURLConnection) new URL(Configure.QEURY_QPI).openConnection();
			// 加入数据
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);

			BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
			buffOutStr.write(orderInfo.getBytes());
			buffOutStr.flush();
			buffOutStr.close();

			// 获取输入流
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

			String line = null;
			StringBuffer sb = new StringBuffer();
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}

			XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));// 说明3(见文末)
			// 将请求返回的内容通过xStream转换为UnifiedOrderRespose对象
			xStream.alias("xml", ScanPayQueryResData.class);
			ScanPayQueryResData scanPayQueryResData = (ScanPayQueryResData) xStream.fromXML(sb.toString());
			// 根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url
			// <span style="color:#ff0000;"><strong>说明4(见文末)</strong></span>

			if (null != scanPayQueryResData && "SUCCESS".equals(scanPayQueryResData.getReturn_code())
					&& "SUCCESS".equals(scanPayQueryResData.getResult_code())) {
				// return unifiedOrderRespose.getCode_url();
				return scanPayQueryResData;
			} else {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 2016版签名
	 * 
	 * @Title: getOrderInfoWithSign
	 * @Description: 订单签名（用于移动端app支付）
	 * @param unifiedOrder
	 * @return
	 */
	public static Map<String, Object> getOrderInfoWithSign(UnifiedOrderRespose unifiedOrder) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("appid", unifiedOrder.getAppid());
		params.put("partnerid", unifiedOrder.getMch_id());
		params.put("prepayid", unifiedOrder.getPrepay_id());
		params.put("package", "Sign=WXPay");
		params.put("noncestr", unifiedOrder.getNonce_str());
		params.put("timestamp", System.currentTimeMillis() / 1000);
		String sign = Signature.getSign(params);
		params.put("sign", sign);
		return params;
	}

	/**
	 * 2015版签名
	 * 
	 * @Title: getOrderInfoWithSign
	 * @Description: 订单签名（用于移动端app支付）
	 * @param unifiedOrder
	 * @return
	 */
	public static Map<String, Object> getOrderInfoWithAndroidSign(UnifiedOrderRespose unifiedOrder) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("appid", unifiedOrder.getAppid());
		params.put("partnerid", unifiedOrder.getMch_id());
		params.put("prepayid", unifiedOrder.getPrepay_id());
		params.put("package", "Sign=WXPay");
		params.put("noncestr", unifiedOrder.getNonce_str());
		params.put("timestamp", "1");
		String sign = AppSignUtil.genAppSign(params);
		params.put("sign", sign);
		return params;
	}

	public static String getOrderSingWithOld(UnifiedOrderRespose unifiedOrder) {
		List<NameValuePair> signParams = new LinkedList<NameValuePair>();
		signParams.add(new BasicNameValuePair("appid", unifiedOrder.getAppid()));
		signParams.add(new BasicNameValuePair("noncestr", unifiedOrder.getNonce_str()));
		signParams.add(new BasicNameValuePair("package", "Sign=WXPay"));
		signParams.add(new BasicNameValuePair("partnerid", unifiedOrder.getMch_id()));
		signParams.add(new BasicNameValuePair("prepayid", unifiedOrder.getPrepay_id()));
		signParams.add(new BasicNameValuePair("timestamp", String.valueOf(System.currentTimeMillis() / 1000)));
		return AppSignUtil.genAppSign(signParams);
	}

	/**
	 * 功能描述:请求退款服务https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6
	 * 作者:温海金
	 * 
	 * @param refundReqData
	 *            这个数据对象里面包含了API要求提交的各种数据字段, 可以通过构造方法创建RefundReqData(String
	 *            transactionID,String outTradeNo,String deviceInfo,String
	 *            outRefundNo,int totalFee,int refundFee,String opUserID,String
	 *            refundFeeType)
	 * @param transactionID
	 *            是微信系统为每一笔支付交易分配的订单号，通过这个订单号可以标识这笔交易，它由支付订单API支付成功时返回的数据里面获取到。建议优先使用
	 * @param outTradeNo
	 *            商户系统内部的订单号,transaction_id 、out_trade_no
	 *            二选一，如果同时存在优先级：transaction_id>out_trade_no
	 * @param deviceInfo
	 *            微信支付分配的终端设备号，与下单一致
	 * @param outRefundNo
	 *            商户系统内部的退款单号，商户系统内部唯一，同一退款单号多次请求只退一笔
	 * @param totalFee
	 *            订单总金额，单位为分
	 * @param refundFee
	 *            退款总金额，单位为分,可以做部分退款
	 * @param opUserID
	 *            操作员帐号, 默认为商户号
	 * @param refundFeeType
	 *            货币类型，符合ISO 4217标准的三位字母代码，默认为CNY（人民币）
	 * @return API返回的RefundResData数据对象 最后更改时间 : 2017年5月22日 下午4:32:00
	 * @throws Exception
	 */
	public static RefundResData requestRefundService(RefundReqData refundReqData) throws Exception {
		String refundServiceResponseString = new RefundService().request(refundReqData);
		return (RefundResData) Util.getObjectFromXML(refundServiceResponseString, RefundResData.class);
	}

	/**
	 * 功能描述:请求退款查询服务https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_5&index=7
	 * 作者:温海金
	 * 
	 * @param refundQueryReqData
	 *            这个数据对象里面包含了API要求提交的各种数据字段
	 * @return API返回的RefundQueryResData数据对象
	 * @throws Exception
	 *             最后更改时间 : 2017年5月22日 下午4:32:00
	 */
	public static RefundQueryResData requestRefundQueryService(RefundQueryReqData refundQueryReqData) throws Exception {
		String refundQueryResDataStr = new RefundQueryService().request(refundQueryReqData);
		return (RefundQueryResData) Util.getObjectFromXML(refundQueryResDataStr, RefundQueryResData.class);
	}

	/**
	 * 功能描述:请求退款服务 作者:温海金
	 * 
	 * @param outTradeNo
	 *            商户系统内部的订单号,transaction_id 、out_trade_no
	 *            二选一，如果同时存在优先级：transaction_id>out_trade_no
	 * @param outRefundNo
	 *            商户系统内部的退款单号，商户系统内部唯一，同一退款单号多次请求只退一笔
	 * @param totalFee
	 *            订单总金额，单位为分
	 * @param refundFee
	 *            退款总金额，单位为分,可以做部分退款 最后更改时间 : 2017年5月22日 下午4:32:00
	 */
	public static RefundResData requestRefundService(String outTradeNo, String outRefundNo, int totalFee,
			int refundFee) {
		RefundReqData refundReqData = new RefundReqData(null, outTradeNo, null, outRefundNo, totalFee, refundFee,
				Configure.getMchid(), "CNY");
		try {
			return requestRefundService(refundReqData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 功能描述:请求退款查询服务 作者:温海金 最后更改时间 : 2017年5月22日 下午4:32:00
	 */
	public static RefundQueryResData requestRefundQueryService(String outTradeNo, String outRefundNo) {
		RefundQueryReqData refundQueryReqData = new RefundQueryReqData(null, outTradeNo, null, outRefundNo, null);
		try {
			return requestRefundQueryService(refundQueryReqData);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}
